-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Makes GetChildKeys more efficient #67186
Conversation
Tagging subscribers to this area: @dotnet/area-extensions-configuration Issue DetailsDetailsPrevents Fixes #62510 BenchmarksBenchmark code: link to gist
|
@adamsitnik not sure if I need to change a setting for the |
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
Can you also add a performance test for keys that do need the split? |
As you suspected the commit was not optimized for the keys needing split. Now with the newest diff I changed it to be optimized for split case. Using: {
// in Setup:
var splittingKeys = new Dictionary<string, string>() { };
for (int i = 1000; i < 2000; i++)
{
splittingKeys.Add("a:" + i.ToString(), string.Empty);
}
_chainedConfigWithSplitting = new ChainedConfigurationProvider(new ChainedConfigurationSource
{
Configuration = new ConfigurationBuilder()
.Add(new MemoryConfigurationSource { InitialData = splittingKeys })
.Build(),
ShouldDisposeConfiguration = false,
});
}
[Benchmark]
public void AddChainedConfigurationWithSplitting()
=> _chainedConfigWithSplitting.GetChildKeys(new string[0], null);
|
Should your perf tests get added to the perf repo? BTW you can copy paste the results tables into an issue like this as they are valid markdown. That’s easier than pasting screenshots. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits.
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
y = yParts[i]; | ||
if (x!.Contains(ConfigurationPath.KeyDelimiter) && y!.Contains(ConfigurationPath.KeyDelimiter)) | ||
{ | ||
string[] xParts = x!.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @tarekgh.
BTW it's a pity that the code which is consuming this type is using string to represent a complex type and uses indexof/split back and forth.
The index is known here:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 98 in 6033ef2
int indexOf = key.IndexOf(ConfigurationPath.KeyDelimiter, prefixLength, StringComparison.OrdinalIgnoreCase); |
then a substring is performed:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 99 in 6033ef2
return indexOf < 0 ? key.Substring(prefixLength) : key.Substring(prefixLength, indexOf - prefixLength); |
then all the logic is reversed to be able to sort the keys:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 91 in 6033ef2
results.Sort(ConfigurationKeyComparer.Comparison); |
It's just a digression, I don't expect @maryamariyan to fix it in this PR (these types are public)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if I need to change a setting for the MultimodalDistribution warning showing up in the screenshot
You don't need to change anything. This benchmark just seems to be multimodal.
y = yParts[i]; | ||
if (x!.Contains(ConfigurationPath.KeyDelimiter) && y!.Contains(ConfigurationPath.KeyDelimiter)) | ||
{ | ||
string[] xParts = x!.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @tarekgh.
BTW it's a pity that the code which is consuming this type is using string to represent a complex type and uses indexof/split back and forth.
The index is known here:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 98 in 6033ef2
int indexOf = key.IndexOf(ConfigurationPath.KeyDelimiter, prefixLength, StringComparison.OrdinalIgnoreCase); |
then a substring is performed:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 99 in 6033ef2
return indexOf < 0 ? key.Substring(prefixLength) : key.Substring(prefixLength, indexOf - prefixLength); |
then all the logic is reversed to be able to sort the keys:
runtime/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationProvider.cs
Line 91 in 6033ef2
results.Sort(ConfigurationKeyComparer.Comparison); |
It's just a digression, I don't expect @maryamariyan to fix it in this PR (these types are public)
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Günther Foidl <gue@korporal.at>
With new changes, Without diff:
With changes:
For |
Should your benchmarks above be added to dotnet/performance to protect your change from regressions? |
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
I plan to add those. Are you recommending to add them before this PR goes in? |
That's your call. Adding them before the main PR (at least say half a day before) has the nice effect of gathering numbers before the change, so you see a nice uptick in the graph when your change goes in, and if it regresses it might possibly be interesting what the original value was. I don't think it matters otherwise. |
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
Found certain use cases for which replacing The replacement function needs to behave similar to One way forward would be to make use of something like the Vaguely something like: var splitterX = spanX.Split(',', StringSplitOptions.RemoveEmptyEntries);
var splitterY = spanY.Split(',', StringSplitOptions.RemoveEmptyEntries);
while (splitterX.TryMoveNext(out var sliceX) && splitterY.TryMoveNext(out var sliceY))
{
// then compare sliceX with sliceY
}
// return splitterX.Length - splitterY.Length UPDATE Seems like |
This looks a good idea to use. |
- Also fixing a regression from last commit + adds test
I first tried using Without diff:
UPDATED TO LATEST:
|
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We know that xDelimiterIndex
is at a delimiter. There is no reason to slice to that location, you can skip past it.
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationKeyComparer.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. Thanks for the great work here, @maryamariyan.
Details
Prevents
ChainedConfigurationProvider.GetChildKeys
from splitting strings or making unnecessary allocation when keys do not contain delimiter or when keys are empty.Fixes #62510
Benchmarks
Benchmark code: link to gist
Also dotnet/performance PR: dotnet/performance#2336
Without diff:
UPDATED TO LATEST:
With latest PR diff (updated using b128f63):